package com.bosssoft.observer;

import com.bosssoft.exception.MyException;
import com.bosssoft.utils.UtilsFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * @author 戴圣燃
 * 观察者模式的核心类，用于管理观察者和通知观察者
 */
@Slf4j
public class Subject {
    private List<Observer> observers = new ArrayList<>();

    /**
     * 添加观察者
     * @param observer 观察者对象
     */
    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    /**
     * 移除观察者
     * @param observer 观察者对象
     */
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    /**
     * 通知所有观察者
     * @param message 通知消息
     */
    public void notifyObservers(String message) {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }

    /**
     * 启动程序并通知观察者
     */
    public void start() {
        // 执行某些操作

        // 通知观察者
        notifyObservers("程序已启动！");
    }

    /**
     * 向服务端发送数据
     *
     * @param socket 用于与服务端建立连接的Socket对象
     * @param path 需要发送到服务端的文件路径
     * @throws IOException 如果发生IO错误
     */
    public static void sendDataToServer(Socket socket, String path, String id) throws IOException {
        // 获取Socket的输出流，用于向服务端发送数据
        OutputStream outputStream = socket.getOutputStream();

        try (// 定义缓冲区并读取文件内容
             BufferedReader reader = new BufferedReader(new FileReader(path));){
            String line;
            StringBuilder sb = new StringBuilder("");
            sb.append(id).append("\n");
            // 提取文件名
            String fileName = path.substring(path.lastIndexOf("\\")+1);
            sb.append(fileName).append("\n");
            // 读取文件每行内容并存储到StringBuilder中
            while ((line = reader.readLine()) != null) {
                sb.append(line).append("\n");
            }
            // 对StringBuilder内容进行加密
            UtilsFactory utilsFactory = new UtilsFactory();
            String s = sb.toString();
            String encode = utilsFactory.useUtil("encrypt").useUtil(s);
            // 将加密后的内容转换为字节数组并发送给服务端
            outputStream.write(encode.getBytes(),0,encode.getBytes().length);
        }
        finally {
            // 确保输出流被关闭
        }
        // 刷新输出流，确保所有数据都被发送
        outputStream.flush();
        // 关闭Socket的输出流，表示发送数据完成
        socket.shutdownOutput();
    }

    /**
     * 从服务端接收数据
     *
     * @param socket 用于与服务端通信的Socket对象
     * @throws IOException 如果发生IO错误
     */
    public static void receiveDataFromServer(Socket socket) throws IOException {
        try  (InputStream inputStream = socket.getInputStream();
              BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));){
            // 从服务端接收消息并打印
            String msg;
            while ((msg = bufferedReader.readLine()) != null) {

                if(msg.equals("已发送完成")){
                    log.info("收到服务端消息：{}", msg);
                }else{
                    // 创建解密工具实例
                    UtilsFactory utilsFactory = new UtilsFactory();
                    // 解密并处理接收到的消息，分离文件名和内容
                    String decode = utilsFactory.useUtil("decrypt").useUtil(msg);
                    String clientId = StringUtils.substringBefore(decode,"\n");

                    String fileNameTmp = StringUtils.substringBefore(StringUtils.substringAfter(decode,"\n"),"\n");
                    String fileName = "D:\\ruijiwaimai\\bosssoft1\\clientReceivedFiles\\"+fileNameTmp;
                    String content = StringUtils.substringAfter(StringUtils.substringAfter(decode,"\n"),"\n");
                    // 将文件内容写入本地文件
                    File file = new File(fileName);
                    OutputStream os = new FileOutputStream(file);
                    os.write(content.getBytes());
                    log.info("已收到id为" + clientId + "的客户端传输文件："+fileNameTmp);
                }
            }
        }catch(Exception e){
            // 异常处理
        }
    }

    /**
     * 从控制台接收指令并处理
     * @return 指令处理结果
     * @throws IOException 如果发生IO错误
     * @throws MyException 如果指令错误
     */
    public static String scannerInstruct() throws IOException, MyException {
        Scanner sc = new Scanner(System.in); // 创建Scanner对象用于读取用户输入
        String s = sc.nextLine(); // 读取一行用户输入
        // 检查输入长度是否小于等于5个字符
        if(s.length()<=5){
            log.info("指令错误！请重新输入：");
            return "error";
        }
        // 分割输入字符串为前5个字符和剩余部分
        String s1 = s.substring(0,5);
        String s2 = s.substring(5,s.length());
        // 从输入字符串中提取文件类型标签（如.xml或.json）
        String label = s.substring(s.lastIndexOf(".")+1);
        UtilsFactory utilsFactory = new UtilsFactory();
        // 处理显示指令
        if(s1.equals("show ")){
            String result = utilsFactory.useUtil(label).useUtil(s2);
            // 根据文件类型标签选择相应的解析器进行解析
            if(!result.equals("error")){
                return "show";
            }else{
                return "error";
            }
        } else if (s1.equals("send ")) { // 处理发送指令
            return s;
        } else {
            log.info("指令错误！请重新输入：");
            return "error";
        }
    }


    public void sendDataToClient(Socket socket, String path, String id, String toClient) throws IOException {
        // 获取Socket的输出流，用于向服务端发送数据
        OutputStream outputStream = socket.getOutputStream();

        try (BufferedReader reader = new BufferedReader(new FileReader(path));){
            // 定义缓冲区并读取文件内容
            String line;
            StringBuilder sb = new StringBuilder("");
            sb.append(toClient);
            sb.append(id).append("\n");
            // 提取文件名
            String fileName = path.substring(path.lastIndexOf("\\")+1);
            sb.append(fileName + "\n");
            // 读取文件每行内容并存储到StringBuilder中
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
            // 对StringBuilder内容进行加密
            UtilsFactory utilsFactory = new UtilsFactory();
            String s = sb.toString();
            String encode = utilsFactory.useUtil("encrypt").useUtil(s);
            // 将加密后的内容转换为字节数组并发送给服务端
            outputStream.write(encode.getBytes(),0,encode.getBytes().length);
        } finally {
            // 确保输出流被关闭
        }
        // 刷新输出流，确保所有数据都被发送
        outputStream.flush();
        // 关闭Socket的输出流，表示发送数据完成
        socket.shutdownOutput();
    }

}

